// SPDX-FileCopyrightText: Copyright 2025-2025 深圳市同心圆网络有限公司
// SPDX-License-Identifier: GPL-3.0-only

package service_impl

import (
	"fmt"
	"io"
	"strconv"
	"strings"

	"gitcode.com/openseaotter/so_server/storage"
	"github.com/gin-gonic/gin"
)

func ProcessStatBlob(c *gin.Context) {
	groupName := c.Param("group")
	digest := c.Param("digest")

	exist, err := storage.ImageGroupDao.Exist(c.Request.Context(), groupName)
	if err != nil {
		SendError(c, 400, ERROR_CODE_NAME_UNKNOWN, err.Error())
		return
	}
	if !exist {
		SendError(c, 403, ERROR_CODE_DENIED, "image group not exist")
		return
	}
	algo, err := GetDigestAlgo(digest)
	if err != nil {
		SendError(c, 400, ERROR_CODE_DIGEST_INVALID, err.Error())
		return
	}

	blobFile, err := storage.BlobStore.Open(digest)
	if err != nil {
		SendError(c, 404, ERROR_CODE_BLOB_UNKNOWN, err.Error())
		return
	}
	defer blobFile.Close()

	blobLen, err := storage.BlobStore.Length(digest)
	if err != nil {
		SendError(c, 404, ERROR_CODE_BLOB_UNKNOWN, err.Error())
		return
	}

	contentDigest, err := GenDigest(algo, blobFile)
	if err != nil {
		SendError(c, 400, ERROR_CODE_DIGEST_INVALID, err.Error())
		return
	}
	c.Header("Content-Length", fmt.Sprintf("%d", blobLen))
	c.Header("Docker-Content-Digest", contentDigest)
	c.Status(200)
}

func ProcessRemoveBlob(c *gin.Context) {
	groupName := c.Param("group")
	digest := c.Param("digest")

	groupInfo, err := storage.ImageGroupDao.Get(c.Request.Context(), groupName)
	if err != nil {
		SendError(c, 403, ERROR_CODE_DENIED, "image group not exist")
		return
	}
	//检查用户是否可以push
	secName := GetSecretUsername(c)
	allow := false
	for _, secretName := range groupInfo.PermInfo.PushSecretNameList {
		if secName == secretName {
			allow = true
			break
		}
	}
	if !allow {
		SendError(c, 403, ERROR_CODE_DENIED, "not allow push")
		return
	}

	blobFile, err := storage.BlobStore.Open(digest)
	if err != nil {
		SendError(c, 404, ERROR_CODE_BLOB_UNKNOWN, err.Error())
		return
	}
	defer blobFile.Close()

	err = storage.BlobStore.Remove(digest)
	if err != nil {
		SendError(c, 400, ERROR_CODE_BLOB_UNKNOWN, err.Error())
		return
	}

	c.Status(202)
}

func ProcessDownloadBlob(c *gin.Context) {
	groupName := c.Param("group")
	digest := c.Param("digest")
	rangeStr := c.GetHeader("Range")
	rangeStart := 0
	rangeEnd := 0
	var err error

	if strings.HasPrefix(rangeStr, "bytes=") {
		remainStr := rangeStr[6:]
		parts := strings.Split(remainStr, "-")
		if len(parts) != 2 {
			SendError(c, 400, ERROR_CODE_NAME_INVALID, "wrong range header")
			return
		}
		rangeStart, err = strconv.Atoi(parts[0])
		if err != nil {
			SendError(c, 400, ERROR_CODE_NAME_INVALID, err.Error())
			return
		}
		rangeEnd, err = strconv.Atoi(parts[1])
		if err != nil {
			SendError(c, 400, ERROR_CODE_NAME_INVALID, err.Error())
			return
		}
		if rangeEnd <= rangeStart {
			SendError(c, 400, ERROR_CODE_NAME_INVALID, "wrong range header")
			return
		}
	}

	groupInfo, err := storage.ImageGroupDao.Get(c.Request.Context(), groupName)
	if err != nil {
		SendError(c, 403, ERROR_CODE_DENIED, "image group not exist")
		return
	}
	//检查用户是否可以pull
	secName := GetSecretUsername(c)
	allow := false
	for _, secretName := range groupInfo.PermInfo.PullSecretNameList {
		if secName == secretName {
			allow = true
			break
		}
	}
	if !allow {
		SendError(c, 403, ERROR_CODE_DENIED, "not allow pull")
		return
	}

	blobReader, err := storage.BlobStore.Open(digest)
	if err != nil {
		SendError(c, 404, ERROR_CODE_NAME_INVALID, err.Error())
		return
	}
	defer blobReader.Close()

	if strings.HasPrefix(rangeStr, "bytes=") {
		_, err = blobReader.Seek(int64(rangeStart), io.SeekStart)
		if err != nil {
			SendError(c, 400, ERROR_CODE_NAME_INVALID, err.Error())
			return
		}
		data := make([]byte, rangeEnd-rangeStart)
		n, err := blobReader.Read(data)
		if err != nil {
			SendError(c, 400, ERROR_CODE_NAME_INVALID, err.Error())
			return
		}
		if n != rangeEnd-rangeStart {
			SendError(c, 400, ERROR_CODE_NAME_INVALID, "wrong range")
			return
		}
		len, err := storage.BlobStore.Length(digest)
		if err != nil {
			SendError(c, 400, ERROR_CODE_NAME_INVALID, err.Error())
			return
		}
		// c.Header("Docker-Content-Digest", digest)
		c.Header("Content-Range", fmt.Sprintf("bytes %d-%d/%d", rangeStart, rangeEnd, len))
		c.Data(206, "application/octet-stream", data)
	} else {
		data, err := io.ReadAll(blobReader)
		if err != nil {
			SendError(c, 400, ERROR_CODE_NAME_INVALID, err.Error())
			return
		}
		c.Header("Docker-Content-Digest", digest)
		c.Data(200, "application/octet-stream", data)
	}
}
